package com.example.media.aspect;

import com.example.media.bloom.RedisBloomFilter;
import com.example.media.constant.RedisKey;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class BloomAspect {

    private final RedisTemplate redisTemplate;
    private final RedisBloomFilter redisBloomFilter;
    private final ObjectMapper objectMapper;


    @Pointcut("@annotation(com.example.media.annotation.BloomFilter)")
    public void bloomfilter() {


    }


    @Before("bloomfilter()")
    public void beforeLog(JoinPoint point) throws JsonProcessingException {

    }


    @Around("bloomfilter()")
    public Object aroundLog(ProceedingJoinPoint point) throws Throwable {
        Object[] args = point.getArgs();
        String value = objectMapper.writeValueAsString(args);
        if (redisBloomFilter.contains(RedisKey.BLOOM_FILTER, value)) {
            if (redisTemplate.hasKey(RedisKey.Api_CACHE + value)) {
                String o = (String) redisTemplate.opsForValue().get(RedisKey.Api_CACHE + value);
                Map map = objectMapper.readValue(o, Map.class);
                Object body = map.get("body");
                return ResponseEntity.ok(body);
            } else {
                if (redisTemplate.opsForValue().setIfAbsent(RedisKey.Api_CACHE + value + "lock", 1, 10, TimeUnit.SECONDS)) {
                    Object result = point.proceed();
                    redisTemplate.opsForValue().set(RedisKey.Api_CACHE + value, objectMapper.writeValueAsString(result), 3, TimeUnit.MINUTES);
                    redisTemplate.delete(RedisKey.Api_CACHE + args + "lock");
                    return result;
                } else {
                    return aroundLog(point);
                }

            }

        } else {
            redisBloomFilter.add(RedisKey.BLOOM_FILTER, value);
            if (redisTemplate.opsForValue().setIfAbsent(RedisKey.Api_CACHE + value + "lock", 1, 10, TimeUnit.SECONDS)) {
                Object result = point.proceed();
                redisTemplate.opsForValue().set(RedisKey.Api_CACHE + value, objectMapper.writeValueAsString(result), 3, TimeUnit.MINUTES);
                redisTemplate.delete(RedisKey.Api_CACHE + args + "lock");
                return result;
            } else {
                return aroundLog(point);
            }
        }

    }


    @AfterReturning(value = "bloomfilter()", returning = "returnObject")
    public void afterReturning(Object returnObject) {

    }


}
